#include "cmd.h"
#include "file_cmd.h"
#include "pe.h"
#include "printers.h"
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>

static void s_show_splash()
{
	wprintf(L"pev - A managed PE viewer. Version 1.2 Stable.\n");
	wprintf(L"(c) Anders Liu.\n\n");
	wprintf(L"For help, type '?', 'h' or 'help'.\n");
}

#define S_FILE_CONTENT_BUF_LEN 1024

static pe_file cur_pe;

/*
 * Show the error of parsed cmd.
 *
 * Parameters:
 *   err_cmd - The error cmd.
 *
 * Returns: None.
 */
static void s_show_cmd_error(const cmd *err_cmd)
{
	if(err_cmd->err_msg != NULL)
		wprintf(L"%s\n", err_cmd->err_msg);
}

/*
 * Entrypoint.
 */
int wmain(int argc, wchar_t *argv[])
{
	wchar_t cmd_buf[MAX_CMD_LN_LEN];
	cmd cur_cmd;

	if(!CHECK_INT_SIZE())
	{
		wprintf(L"Invalid integer size, program cannot run.\n");
		return -1;
	}

	s_show_splash();

	// Main loop
	for(;;)
	{
		// Read command line.
		int cmd_len = 0;

		wprintf(L"-----------------------------------------------------------------\n");
		wprintf(get_cur_file_title());
		wprintf(L"> ");
		fgetws(cmd_buf, MAX_CMD_LN_LEN, stdin);
		while(cmd_buf[cmd_len] != L'\n' && cmd_buf[cmd_len] != L'\0')
			++cmd_len;
		cmd_buf[cmd_len] = L'\0';

		wprintf(L"\n");

		// Parse command.
		parse_cmd(cmd_buf, &cur_cmd);

		if(cur_cmd.id == CMD_EXIT)
			break;  // Exit main loop.

		// Execute command.
		switch(cur_cmd.id)
		{
		case CMD_EMPTY:          break;
		case CMD_ERROR:          s_show_cmd_error(&cur_cmd);             break;
		case CMD_HELP:           show_cmd_help();                        break;
		case CMD_OPEN:           exec_open(cur_cmd.str_arg, &cur_pe);    break;
		case CMD_REFRESH:        exec_refresh(&cur_pe);                  break;
		case CMD_FILE:           exec_file();                            break;
		case CMD_DUMP_DATA:      exec_dump_data(cur_cmd.range_start, cur_cmd.range_len); break;
		case CMD_RVA:            exec_rva(&cur_pe, cur_cmd.szt_arg);     break;
		case CMD_OFFSET:         exec_offset(&cur_pe, cur_cmd.szt_arg);  break;

		case CMD_HD_DOS:         print_dos_header      (&cur_pe); break;
		case CMD_HD_PE_SIG:      print_pe_sig          (&cur_pe); break;
		case CMD_HD_FILE:        print_file_header     (&cur_pe); break;
		case CMD_HD_OPTIONAL:    print_optional_header (&cur_pe); break;
		case CMD_HD_SECTIONS:    print_section_headers (&cur_pe); break;
		case CMD_HD_NT:          print_nt_headers      (&cur_pe); break;

		case CMD_MD_CLIH:        print_cli_header     (&cur_pe); break;
		case CMD_MD_ST_SIG:      print_md_storage_sig (&cur_pe); break;
		case CMD_MD_ST_HDR:      print_md_storage_hdr (&cur_pe); break;
		case CMD_MD_STREAM_HDRS: print_md_stream_hdrs (&cur_pe); break;
		case CMD_MD_HDRS:        print_md_headers     (&cur_pe); break;

		case CMD_MD_STR:         print_md_strings (&cur_pe, cur_cmd.range_start); break;
		case CMD_MD_US:          print_md_us      (&cur_pe, cur_cmd.range_start); break;
		case CMD_MD_GUID:        print_md_guid    (&cur_pe, cur_cmd.range_start); break;
		case CMD_MD_BLOB:        print_md_blob    (&cur_pe, cur_cmd.range_start); break;

		case CMD_MD_SNSIG:       print_md_sn_signature(&cur_pe); break;
		case CMD_MD_RES:         print_md_res(&cur_pe, cur_cmd.range_start, cur_cmd.range_len);     break;
		case CMD_MD_VTFIXUP:     print_md_vtfixup(&cur_pe, cur_cmd.range_start, cur_cmd.range_len); break;

		case CMD_MDT_HDR:        print_mdt_hdr(&cur_pe);         break;
		case CMD_MDT_LIST:       print_mdt_list(&cur_pe, FALSE); break;
		case CMD_MDT_LIST_WC:    print_mdt_list(&cur_pe, TRUE);  break;
		case CMD_MDT_CT:         print_mdt_ct(&cur_pe, FALSE);   break;
		case CMD_MDT_CTRT:       print_mdt_ct(&cur_pe, TRUE);    break;
		case CMD_MDT_T:
		case CMD_MDT_TI:
			print_mdt_tbl(&cur_pe, cur_cmd.tid_arg,
				cur_cmd.range_start, cur_cmd.range_len, TRUE);
			break;

		case CMD_IL_FULL:        print_il_method(&cur_pe, cur_cmd.szt_arg, TRUE);  break;
		case CMD_IL_HDR:         print_il_method(&cur_pe, cur_cmd.szt_arg, FALSE); break;

		default: wprintf(L"Unhandled command: %d\n", cur_cmd.id); break;
		}

		wprintf(L"\n");
	}
}
